package Core;

import java.util.Vector;
import javax.microedition.lcdui.Graphics;

public class LayoutManager extends Control
{
	public static final int VERTICAL = 0, HORIZONTAL = 1, GRID = 2;
	public int type = VERTICAL;

	public Form parent;
	public Img bg;

	public int spacing = 2;
	public int h_border = 0;
	public int v_border = 0;

	public boolean scrollEnable = false;
	public boolean allowToEnlarge = true;
	public boolean allowMovement = true;
	public boolean flexible = false;

	protected Vector controls = new Vector();
	protected int additionalHeight = 0;

	private int spacers = 0;
	private int spacer_l = 0;
	private int scrollX = 0, scrollY=0;

	private Control hotControls[] = new Control[Key.KEYS_TOTAL];

	private Rect rects [] = new Rect [0];
	
	public LayoutManager(int w, int h)
	{
		W = w;
		H = h;
	}

	public LayoutManager()
	{}

	public void setSelectable(boolean selectable)
	{
		this.selectable = selectable;
	}

	public void setParentForm(Form f)
	{
		super.setParentForm(f);

		if(!controls.isEmpty())
		{
			for(int i=0; i<controls.size(); i++)
			{
				((Control)controls.elementAt(i)).setParentForm(parentForm);
			}
		}
	}

	public int getMinH()
	{
		int height = 0;
		if(type == VERTICAL)
		{
			for(int i = 0; i<controls.size(); i++)
			{
				height += ((Control)controls.elementAt(i)).getH() + spacing;
			}
			height -= spacing;
			height -= spacing*spacers;
		}
		else for(int i = 0; i<controls.size(); i++) if(height < ((Control)controls.elementAt(i)).getH()) height = ((Control)controls.elementAt(i)).getH();
		return height + v_border*2;
	}

	protected int getMinW()
	{
		int width = 0;
		if(type == HORIZONTAL)
		{
			for(int i = 0; i<controls.size(); i++)
			{
				width += ((Control)controls.elementAt(i)).getW() + spacing;
			}
			width -= spacing;
			width -= spacing*spacers;
		}
		else for(int i = 0; i<controls.size(); i++) if(width < ((Control)controls.elementAt(i)).getW()) width = ((Control)controls.elementAt(i)).getW();
		return width + h_border*2;
	}

	public Vector getControls()
	{
		return controls;
	}

	public void updateDimensions()
	{
		if(allowToEnlarge)
		{
			W = getMinW() > W ? getMinW() : W;
			H = getMinH() > H ? getMinH() : H;
		}
		else if(flexible)
		{
			H = getMinH();
			W = getMinW();
		}

		if(parentManager != null) parentManager.updateDimensions();
	}

	public Control addControl(Control c)
	{
		if(c == null) return null;

		controls.addElement(c);
		c.setParentForm(parentForm);
		c.setParentManager(this);

		if(c instanceof Spacer) spacers++;
		updateDimensions();

		return c;
	}

	public Control removeControl(Control c)
	{
		Control res = null;
		if(controls.contains(c))
		{
			res = c;
			controls.removeElement(c);
		}
		return res;
	}

	public Control insertControlBefore(Control what, Control before)
	{
		if(controls.contains(before) && what != null)
		{
			System.out.println("insert");

			controls.insertElementAt(what, controls.indexOf(before));
			what.setParentForm(parentForm);
			updateDimensions();

			return what;
		}

		return null;
	}

	public Control insertControlAfter(Control what, Control after)
	{
		if(controls.contains(after) && what != null)
		{
			controls.insertElementAt(what, controls.indexOf(after)+1);

			what.setParentForm(parentForm);
			updateDimensions();

			return what;
		}

		return null;
	}

	public void draw(Graphics g, int x, int y)
	{
		int s = 0;
		Control control;

		if(controls.isEmpty()) return;

		/* сохраняем настройки клипа, чтобы потом восстановить
		int ch = g.getClipHeight();
		int cw = g.getClipWidth();
		int cy = g.getClipY();
		int cx = g.getClipX();

		Bounds bigRect = new Bounds(g.getClipX(), g.getClipY(), g.getClipX()+g.getClipWidth(), g.getClipY()+g.getClipHeight());
		Bounds frameRect = new Bounds(x+h_border, y+v_border, x+getW()-h_border+2, y+getH()-v_border+2);
		frameRect.cut(bigRect);
		*/
		// рисуем фон, если он есть
		if(bg != null) bg.draw(g, x, y);

		if(type == VERTICAL) // элементы сверху-вниз
		{
			// вычисляем размеры распорки
			int height = getMinH();
			if(height >= H || spacers == 0) spacer_l = 0;
			else spacer_l = (H - height - (controls.size() - spacers - 1)*spacing - v_border*2) / spacers;

			if(!scrollEnable)
			{
				// рисуем все элементы
				for(int i = 0; i<controls.size(); i++)
				{
					
					control = (Control) controls.elementAt(i);
					
					if(!(control instanceof Spacer))
					{
						//Bounds smallRect = new Bounds(x+h_border, y+s+v_border, x+h_border+control.getW()+2, y+s+v_border+control.getH()+2);
						//smallRect.cut(frameRect);
						
						//setClip(smallRect, g);
						
						GUICanvas.clipMashine.clip(g, x+h_border, y+s+v_border, control.getW()+2, s+control.getH()+2);

						control.draw(g, x+h_border, y+s+v_border);
						control.coord.x = x+h_border;
						control.coord.y = y+s+v_border;
						s += control.getH() + spacing;

						GUICanvas.clipMashine.removeTopClip(g);
					}
					else s += spacer_l;
				}

				//setClip(bigRect, g);
			}
			else // srollEnable
			{
				// обновляем масив координат если кол-во элементов изменилось
				if(rects.length != controls.size())
				{
					rects = new Rect [controls.size()];
					for(int i=0; i<controls.size(); i++) rects[i] = new Rect();
				}
				
				// получаем координаты всех элементов
				for(int i=0; i<controls.size(); i++)
				{
					// получаем контрол
					control = (Control) controls.elementAt(i);

					if(!(control instanceof Spacer))
					{
						// если контрол - не распорка
						// вычисляем его координаты
						rects[i].left  = h_border;
						rects[i].up    = s+v_border;
						rects[i].right = rects[i].left + control.getW();
						rects[i].down  = rects[i].up + control.getH();
						s += control.getH() + spacing;
					}
					else s += spacer_l; // иначе просто приплюсовываем к смещению высоту распорки
				}

				// получаем координаты активного элемента
				Rect activeRect = rects[getActiveControlIndex()];

				// вычисляем смещение
				if(activeRect.down+scrollY > H) scrollY = H - activeRect.down; 
				if(activeRect.up+scrollY < 0) scrollY = 0 - activeRect.up;

				// рисуем элементы со смещением
				for(int i=0; i<controls.size(); i++) 
				{
					// получаем контрол
					control = (Control) controls.elementAt(i);
					
					/* вычисляем и ставим клип */
					Bounds smallRect = new Bounds(x+rects[i].left, y+rects[i].up + scrollY, x+rects[i].left+control.getW()+spacing, y+rects[i].up + scrollY+control.getH()+spacing);
					/*smallRect.cut(frameRect);
					g.setClip(smallRect.xMin, smallRect.yMin, smallRect.xMax-smallRect.xMin, smallRect.yMax-smallRect.yMin);
					*/

					GUICanvas.clipMashine.clip(g, 
										smallRect.xMin,
										smallRect.yMin,
										smallRect.xMax-smallRect.xMin,
										smallRect.yMax-smallRect.yMin);

					// рисуем контрол
					control.draw(g, x+rects[i].left, y+rects[i].up + scrollY);

					// запоминаем коорлинаты отрисовки для потомков)
					control.coord.x = x+rects[i].left;
					control.coord.y = y+rects[i].up + scrollY;

					GUICanvas.clipMashine.removeTopClip(g);
				}

				// восстанавливаем клип после отрисовки
				//setClip(bigRect, g);
				
				// рисуем скроллер
				if(rects[rects.length-1].down > getH()) 
				{
					for(int i=-1; i<=0; i++)
					{
						g.setColor(0xbebebe);
						g.drawLine(x+getW()-2+i, y, x+getW()-2+i, y+getH());
						g.setColor(0xffffff);
						g.drawLine(x+getW()-2+i, y+ (-scrollY*(getH()-additionalHeight))/rects[rects.length-1].down, x+getW()-2+i, y + (-scrollY*(getH()-additionalHeight))/rects[rects.length-1].down + ((getH()-additionalHeight)*(getH()-additionalHeight))/rects[rects.length-1].down);
					}
				}
				else
				{
					for(int i=-1; i<=0; i++)
					{
						g.setColor(0xbebebe);
						g.drawLine(x+getW()-2+i, y, x+getW()-2+i, y+getH());
					}
				}
			}
		}
		else if(type == HORIZONTAL) // элементы слева-направо
		{
			// вычисляем размер распорки
			int width = getMinW();
			if(width >= W || spacers == 0) spacer_l = 0;
			else spacer_l = (W - width - (controls.size() - spacers - 1)*spacing - h_border*2) / spacers;

			// отрисовываем все элементы
			for(int i = 0; i<controls.size(); i++)
			{
				control = (Control) controls.elementAt(i);
				if(!(control instanceof Spacer))
				{
					//Bounds smallRect = new Bounds(x+s+h_border, y+v_border, x+s+h_border+control.getW()+spacing, y+v_border+control.getH()+spacing);
					//smallRect.cut(frameRect);
					//setClip(smallRect, g);
					GUICanvas.clipMashine.clip(g, x+s+h_border,
												y+v_border,
												control.getW()+spacing,
												control.getH()+spacing);

					control.draw(g, x+s+h_border, y+v_border);
					s+= control.getW() + spacing;

					GUICanvas.clipMashine.removeTopClip(g);
				}
				else
				{
					s+= spacer_l;
				}
			}

			//setClip(bigRect, g);
		}
		else // GRID
		{
			int rowH=0;
			int sx=0, sy=0;

			for(int i = 0; i<controls.size(); i++)
			{
				control = (Control) controls.elementAt(i);

				if(sx + control.getW() > getAvailableW() || control instanceof Spacer)
				{
					sx = 0;
					sy += rowH + spacing;
					rowH = 0;
				}
				
				control.draw(g, x+sx+h_border, y+sy+v_border);
				sx += control.getW() + spacing;
				rowH = rowH < control.getH() ? control.getH() : rowH;
			}
		}

		// восстанавливаем клип
		//g.setClip(cx, cy, cw, ch);
	}

	public int getAvailableW()
	{
		return getW() - h_border*2 - (scrollEnable ? 4 : 0);
	}

	public int getAvailableW(int n)
	{
		if(n>0) return (getAvailableW()-(n-1)*spacing)/n;
		return 0;
	}

	public int getAvailableH()
	{
		return getH() - v_border*2 - (scrollEnable ? 4 : 0);
	}

	public int getAvailableH(int n)
	{
		if(n>0) return (getAvailableH()-(n-1)*spacing)/n;
		return 0;
	}

	public Control getControl(int i)
	{
		if(!controls.isEmpty() && i>=0 && i<controls.size()) return (Control)controls.elementAt(i);
		return null;
	}

	public Control getActiveControl()
	{
		Control c, c2;

		for(int i = 0; i<controls.size(); i++)
		{
			c = (Control) controls.elementAt(i);
			if(c instanceof LayoutManager)
			{
				c2 = ((LayoutManager)c).getActiveControl();
				if(c2 != null) return c;
			}
			else
			{
				if(c.getState() == Control.ACTIVE || c.getState() == Control.PRESSED) return c;
			}
		}

		return null;
	}

	public Control getRealActiveControl()
	{
		Control c, c2;

		for(int i = 0; i<controls.size(); i++)
		{
			c = (Control) controls.elementAt(i);
			if(c instanceof LayoutManager)
			{
				c2 = ((LayoutManager)c).getActiveControl();
				if(c2 != null) return c2;
			}
			else
			{
				if(c.getState() == Control.ACTIVE || c.getState() == Control.PRESSED) return c;
			}
		}

		return null;
	}

	private int getActiveControlIndex()
	{
		Control c = getActiveControl();
		if(c == null) return 0;
		else return controls.indexOf(c);
	}

	public boolean select(int what)
	{
		Control c=null, nc=null;

		if(controls.isEmpty()) return false;

		if((what == Control.THIS || what == Control.UNDER || what == Control.RIGHTOF) && getActiveControl() == null)
		{
			//System.out.println(name+": def select first");
			if(!controls.isEmpty()) for(int i=0; i<controls.size(); i++) if(getControl(i).selectable) return getControl(i).select(Control.THIS);
			return false;
		}
		else if((what == Control.UPPER || what == Control.LEFTOF) && getActiveControl() == null)
		{
			//System.out.println(name+": def select last");
			if(!controls.isEmpty()) for(int i=controls.size()-1; i>=0; i--) if(getControl(i).selectable) return getControl(i).select(Control.THIS);
			return false;
		}

		else if(what == Control.RIGHTOF)
		{
			if(type == HORIZONTAL)
			{
				if(getActiveControlIndex() < (controls.size()-1))
				{
					nc = getControl(getActiveControlIndex() + 1);
					c = getActiveControl();
				}
				else
				{
					nc = getControl(0);
					c = getActiveControl();
				}
			}
			else
			{
				getActiveControl().select(Control.RIGHTOF);
			}
		}
		else if(what == Control.LEFTOF)
		{
			if(type == HORIZONTAL)
			{
				if(getActiveControlIndex() > 0)
				{
					nc = getControl(getActiveControlIndex() - 1);
					c = getActiveControl();
				}
				else
				{
					nc = getControl(controls.size()-1);
					c= getActiveControl();
				}
			}
			else
			{
				getActiveControl().select(Control.LEFTOF);
			}
		}
		else if(what == Control.UNDER)
		{
			if(type == VERTICAL) // элементы сверху-вниз
			{
				if(!(getActiveControl() instanceof LayoutManager)) // если выбран реальный элемент
				{
					if(getActiveControlIndex() < (controls.size()-1) && traceControl(getControl(getActiveControlIndex() + 1), what) != null)
					{
						switchSelectTo(getControl(getActiveControlIndex() + 1), what);
						//System.out.println(name + ": switch to next real");
						return false; // остановить последовательность перехода
					}
					else
					{
						//System.out.println(name + ": deselect");
						return true; // разрешить выбрать следующий эл-т
					}
				}
				else // если выбран контейнер
				{
					if(getActiveControl().select(what)) // если контейнер разрешил выбрать след эл-т
					{
						if(getActiveControlIndex() < (controls.size()-1))
						{
							//System.out.println(name + ": switch to next ("+ (getActiveControlIndex()+1) +")");
							switchSelectTo(getControl(getActiveControlIndex()+1), what);
							return false; // остановить последовательность перехода
						}
						else
						{
							switchSelectTo(getControl(0), what);
							//System.out.println(name + ": switch to first");
							return true; // разрешить выбрать следующий эл-т
						}
					}
					return false; // остановить последовательность перехода
				}
			}
			else // элементы справа-налево
			{
				return getActiveControl().select(Control.UNDER);
			}
		}
		else if(what == Control.UPPER)
		{
			if(type == VERTICAL)
			{
				if(!(getActiveControl() instanceof LayoutManager)) // если выбран реальный элемент
				{
					if(getActiveControlIndex() > 0  && traceControl(getControl(getActiveControlIndex() - 1), what) != null)
					{
						switchSelectTo(getControl(getActiveControlIndex() - 1), what);
						return false; // остановить последовательность перехода
					}
					else return true; // разрешить выбрать следующий эл-т
					
				}
				else // если выбран контейнер
				{
					if(getActiveControl().select(what)) // если контейнер разрешил выбрать след эл-т
					{
						if(getActiveControlIndex() > 0)
						{
							switchSelectTo(getControl(getActiveControlIndex()-1), what);
							return false; // остановить последовательность перехода
						}
						else
						{
							switchSelectTo(getControl(controls.size()-1), what);
							return true; // разрешить выбрать следующий эл-т
						}
					}

					return false; // разрешить выбрать предыдущий эл-т
				}
			}
			else
			{
				return getActiveControl().select(Control.UPPER);
			}
		}

		if(nc != null)
		{
			if(!nc.selectable)
			{
				switchSelectTo(nc);
				return select(what);
			}
			else return switchSelectTo(nc);
		}
		else return switchSelectTo(nc);

	}

	private Control traceControl(Control start, int where)
	{
		if(start == null) return null;
		if(where == Control.THIS || where == Control.UNDER || where == Control.RIGHTOF)
		{
			//System.out.println(name+": def select first");
			for(int i=controls.indexOf(start); i<controls.size(); i++) if(getControl(i).selectable) return getControl(i);
			//for(int i=0; i<controls.indexOf(start); i++) if(getControl(i).selectable) return getControl(i);
			return null;
		}
		else
		{
			//System.out.println(name+": def select last");
			for(int i=controls.indexOf(start); i>=0; i--) if(getControl(i).selectable) return getControl(i);
			//for(int i=controls.size()-1; i>=controls.indexOf(start); i--) if(getControl(i).selectable) return getControl(i);
			return null;
		}
	}

	public boolean switchSelectTo(Control next)
	{
		if(next == null) return false;

		Control prev = getActiveControl();
		if(prev != null)
		{
			if(prev.deselect())
			{
				return next.select(THIS);
			}
		}
		return next.select(THIS);
	}

	public boolean switchSelectTo(Control next, int how)
	{
		if(next == null) return false;
		if(controls.isEmpty()) return false;

		Control prev = getActiveControl();
		if(prev != null)
		{
			if(prev.deselect())
			{
				Control n = traceControl(next, how);
				if(n != null) return n.select(how);
				else return false;
			}
		}
		return next.select(how);
	}

	public boolean deselect()
	{
		Control c = getActiveControl();
		if(c != null) return c.deselect();
		else return true;
	}

	public void press()
	{
		Control c = getActiveControl();
		if(c != null)
		{
			c.press();
			if(parent != null) parent.onEvent(GM.CONTROL_PRESSED ,getRealActiveControl());
		}
	}

	public void release()
	{
		Control c = getActiveControl();
		if(c != null)
		{
			c.release();
			if(parent != null) parent.onEvent(GM.CONTROL_RELEASED, getRealActiveControl());
		}
	}

	public void repeate()
	{
		Control c = getActiveControl();
		if(c != null)
		{
			c.repeate();
			if(parent != null) parent.onEvent(GM.CONTROL_REPEATED, getRealActiveControl());
		}
	}

	public void setHotControl(Control c, int key)
	{
		if(c==null || !c.selectable) return;

		key = Key.FromKeyToNumber(key);
		if(key<0 || key>=Key.KEYS_TOTAL) return;
		
		hotControls[key] = c;

		if(key == 0) c.addImage(ControlImage.NOTIFER_0);
		if(key == 1) c.addImage(ControlImage.NOTIFER_1);
		if(key == 2) c.addImage(ControlImage.NOTIFER_2);
		if(key == 3) c.addImage(ControlImage.NOTIFER_3);
		if(key == 4) c.addImage(ControlImage.NOTIFER_4);
		if(key == 5) c.addImage(ControlImage.NOTIFER_5);
		if(key == 6) c.addImage(ControlImage.NOTIFER_6);
		if(key == 7) c.addImage(ControlImage.NOTIFER_7);
		if(key == 8) c.addImage(ControlImage.NOTIFER_8);
		if(key == 9) c.addImage(ControlImage.NOTIFER_9);

		key = Key.FromNumberToKey(key);

		if(key == Key.P_KEY)		c.addImage(ControlImage.NOTIFER_C);
		if(key == Key.LS_KEY)		c.addImage(ControlImage.NOTIFER_LS);
		if(key == Key.RS_KEY)		c.addImage(ControlImage.NOTIFER_RS);
		if(key == Key.POUND_KEY)	c.addImage(ControlImage.NOTIFER_P);
		if(key == Key.STAR_KEY)		c.addImage(ControlImage.NOTIFER_S);
	}

	public void processKey(int key, int state)
	{
		Control active = getRealActiveControl();
		
		if((active == null) || ((active != null) && (!active.lockOn)) || ((active != null) && active.lockOn && (active.getState()!=Control.PRESSED)))
		{
			//System.out.println("LM -> LM");
			if(key == Key.PD_KEY && (state == Key.PRESSED || state == Key.REPEATED) && allowMovement) select(Control.UNDER);
			if(key == Key.PU_KEY && (state == Key.PRESSED || state == Key.REPEATED) && allowMovement) select(Control.UPPER);
			if(key == Key.PL_KEY && (state == Key.PRESSED || state == Key.REPEATED) && allowMovement) select(Control.LEFTOF);
			if(key == Key.PR_KEY && (state == Key.PRESSED || state == Key.REPEATED) && allowMovement) select(Control.RIGHTOF);

			if(key == Key.P_KEY && state == Key.PRESSED) press();
			if(key == Key.P_KEY && state == Key.RELEASED) release();
			if(key == Key.P_KEY && state == Key.REPEATED) repeate();

			key = Key.FromKeyToNumber(key);

			if(key>=0 && key<Key.KEYS_TOTAL)
			{
				if(hotControls[key] != null && state == Key.RELEASED) release();
				if(hotControls[key] != null && state == Key.REPEATED) repeate();
				if(hotControls[key] != null && state == Key.PRESSED)
				{
					switchSelectTo(hotControls[key]);
					press();
				}
			}

			return;
		}

		if((active != null) && (active.getState()==Control.PRESSED))
		{
			//System.out.println("LM -> C");
			active.processKey(key, state);
		}
	}

	private void setClip(Bounds b, Graphics g)
	{
		g.setClip(b.xMin, b.yMin, b.xMax-b.xMin, b.yMax-b.yMin);
	}
}
